package modelo;

import geometria.Coordenada;

import java.util.ArrayList;
import java.util.Iterator;

import ar.uba.fi.algo3.titiritero.ObjetoVivo;
import ar.uba.fi.algo3.titiritero.Posicionable;

/**
 * 
 * Esta clase es la responsable de refenciar a todos los objetos moviles y
 * estaticos, detectar colisiones y aterrizajes.
 * 
 */
public final class Escenario implements Posicionable, ObjetoVivo {

	private Double xMax = 800D;
	private Double yMax = 600D;
	private Double xMin = 0D;
	private Double yMin = 0D;
	private AeroNave seleccionada = null;
	private ArrayList<AeroNave> moviles = new ArrayList<AeroNave>();
	private ArrayList<Pista> estaticos = new ArrayList<Pista>();

	private static Escenario INSTANCE = null;

	private Escenario() {
	}

	private synchronized static void createInstance() {
		if (INSTANCE == null) {
			INSTANCE = new Escenario();
		}
	}

	public static Escenario getInstance() {
		if (INSTANCE == null)
			createInstance();
		return INSTANCE;
	}

	public static void reset() {
		INSTANCE = null;

	}

	/**
	 * Si recibe una coordenada que se encuentra fuera de los limites del
	 * escenario devuelve la coordenada correspondiente de a que los limites
	 * derecho e izquierdo e inferior y superior fueran adyacentes. En otras
	 * palabras: cuando te salis del escenario apareces "del otro lado".
	 * 
	 * @param coordenada
	 *            a ajustar
	 * @return Devuelve una coordenada ajustada al escenario
	 * 
	 */

	public Coordenada ajustarCoordenada(Coordenada c) {

		Double xFinal = c.getX();
		Double yFinal = c.getY();

		if (c.getX() > this.xMax)
			xFinal = c.getX() - this.xMax;
		else if (c.getX() < this.xMin)
			xFinal = c.getX() + this.xMax;

		if (c.getY() > this.yMax)
			yFinal = c.getY() - this.yMax;
		else if (c.getY() < this.yMin)
			yFinal = c.getY() + this.yMax;

		return new Coordenada(xFinal, yFinal);

	}

	public Double getYMax() {
		return yMax;
	}

	public Double getYMin() {
		return yMin;
	}

	public Double getXMax() {
		return xMax;
	}

	public Double getXMin() {
		return xMin;
	}

	public void agregarMovil(AeroNave avion) {
		this.moviles.add(avion);
	}

	public void agregarEstatico(Pista pista) {
		this.estaticos.add(pista);
	}

	public void removerAterrizados() {

		Iterator<AeroNave> i = this.moviles.iterator();
		// Vamos a ver quien aterrizó y lo eliminamos
		i = this.moviles.iterator();
		while (i.hasNext()) {
			AeroNave avion = i.next();
			if (avion.estaAterrizado()) {
				i.remove();
			}
		}

	}

	public boolean hayColision() {
		Iterator<AeroNave> i = this.moviles.iterator();
		AeroNave nave1, nave2;
		// Recorro todas las naves, y las comparo con todas las otras, para ver
		// si algun par colisiona
		while (i.hasNext()) {
			nave1 = i.next();
			Iterator<AeroNave> j = this.moviles.iterator();
			while (j.hasNext()) {
				nave2 = j.next();
				if (nave1.chocoContra(nave2) && (nave1 != nave2))
					return true;
			}
		}
		// Termine de recorrer todo y no hubo colisiones
		return false;

	}

	public Pista pistaEn(Coordenada c) {
		Iterator<Pista> i = this.estaticos.iterator();
		Pista paux;
		while (i.hasNext()) {
			paux = i.next();
			if (paux.contieneAlPunto(c))
				return paux;
		}
		return null;
	}

	public ArrayList<Pista> getEstaticos() {
		return estaticos;
	}

	@Override
	public int getX() {
		return 0;
	}

	@Override
	public int getY() {
		return 0;
	}

	@Override
	public void vivir() {
		this.removerAterrizados();
	}

	/**
	 * Este metodo selecciona la aeronave en la coordenada, si es que existe
	 * 
	 * @param c
	 */
	public boolean seleccionarNave(Coordenada c) {
		Iterator<AeroNave> i = this.moviles.iterator();
		AeroNave naveAux;
		while (i.hasNext()) {
			naveAux = i.next();
			if (naveAux.contienePunto(c)) {
				this.seleccionada = naveAux;
				return true;
			}

		}
		return false;
	}

	public void seleccionarNave(AeroNave nave) {
		this.seleccionada = nave;
	}

	public void deseleccionarNave() {
		this.seleccionada = null;
	}

	public AeroNave getSeleccionada() {
		return this.seleccionada;
	}

	public void recibirClickenCoordenada(Coordenada cAux) {
		if ((!seleccionarNave(cAux) && this.seleccionada != null)) {
			this.seleccionada.agregarPuntoATrayectoria(cAux);
		}

	}

	// Este método lo usamos para deserializar solamente
	static public void set(Escenario e) {
		INSTANCE = e;
	}

	public Iterator<AeroNave> getIteratorNaves() {
		return this.moviles.iterator();
	}

	public int getNavesVolando() {
		return this.moviles.size();
	}

}
